package com.rizzo.analytics.domain.account;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.rizzo.analytics.domain.PersistentEntity;
import com.rizzo.analytics.domain.trigger.Trigger;
import com.rizzo.infrastructure.web.validator.Save;
import com.rizzo.infrastructure.web.validator.Update;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.joda.time.DateTime;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Size;
import javax.validation.groups.Default;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Set;


@Entity
@Table(name = "accounts")
@JsonIgnoreProperties({"triggers", "version", "lastLogin", "lastUpdated"})
public class Account implements Serializable, PersistentEntity {

    public static final int PASSWORD_MAX_LENGTH = 20;
    public static final int PASSWORD_MIN_LENGTH = 6;
    public static final int USERNAME_MAX_LENGTH = 20;
    public static final int EMAIL_MAX_LENGTH = 200;
    public static final int FIRSTNAME_MAX_LENGTH = 30;
    public static final int LASTNAME_MAX_LENGTH = 30;
    public static final int ROLES_MAX_LENGTH = 50;

    public static final String ROLE_ADMIN = "ROLE_ADMIN";
    public static final String ROLE_USER = "ROLE_USER";
    public static final Set<String> POSSIBLE_ROLES = ImmutableSet.of(ROLE_ADMIN, ROLE_USER);

    @Id
    @Null(groups = Save.class, message = "account.id.field.save.present")
    @GeneratedValue
    private Long id;

    @Version
    @JsonIgnore
    private Long version;

    @NotNull(groups = {Default.class, Save.class}, message = "account.username.field.required")
    @Size(min = 1, max = Account.USERNAME_MAX_LENGTH, message = "account.username.field.max.length",
            groups = {Default.class, Save.class, Update.class})
    @Column(unique = true, nullable = false, length = Account.USERNAME_MAX_LENGTH)
    private String username;

    @NotNull(groups = {Default.class, Save.class}, message = "account.email.field.required")
    @Size(min = 1, max = Account.EMAIL_MAX_LENGTH, message = "account.email.field.max.length",
            groups = {Default.class, Save.class, Update.class})
    @Column(unique = true, nullable = false, length = Account.EMAIL_MAX_LENGTH)
    private String email;

    @NotNull(groups = {Default.class, Save.class}, message = "account.firstname.field.required")
    @Size(min = 1, max = Account.FIRSTNAME_MAX_LENGTH, message = "account.firstname.field.max.length",
            groups = {Default.class, Save.class, Update.class})
    @Column(nullable = false, length = Account.FIRSTNAME_MAX_LENGTH)
    private String firstname;

    @NotNull(groups = {Default.class, Save.class}, message = "account.lastname.field.required")
    @Size(min = 1, max = Account.LASTNAME_MAX_LENGTH, message = "account.lastname.field.max.length",
            groups = {Default.class, Save.class, Update.class})
    @Column(nullable = false, length = Account.LASTNAME_MAX_LENGTH)
    private String lastname;

    @NotNull(groups = {Default.class, Save.class}, message = "account.password.field.required")
    @Size(min = Account.PASSWORD_MIN_LENGTH, max = Account.PASSWORD_MAX_LENGTH, message = "account.password.field.min.max.length",
            groups = {Default.class, Save.class, Update.class})
    @Column(nullable = false, length = Account.PASSWORD_MAX_LENGTH)
    private String password;

    @NotNull(groups = {Default.class, Save.class}, message = "account.roles.field.required")
    @Size(min = 1, max = Account.ROLES_MAX_LENGTH, message = "account.roles.field.max.length",
            groups = {Default.class, Save.class, Update.class})
    //	TODO find and enable me
    // @Pattern(regexp = ROLE_USER + "|" + ROLE_ADMIN + "|", message = "account.roles.invalid.content", groups = {Default.class, Save.class, Update.class})
    @Column(nullable = false, length = Account.ROLES_MAX_LENGTH)
    private String roles;

    @NotNull(groups = {Default.class, Save.class}, message = "account.locked.field.required")
    @Column(nullable = false)
    private Boolean locked;

    @NotNull(groups = {Default.class, Save.class}, message = "account.expired.field.required")
    @Column(nullable = false)
    private Boolean expired;

    @NotNull(groups = {Default.class, Save.class}, message = "account.disabled.field.required")
    @Column(nullable = false)
    private Boolean disabled;

    @JsonIgnore
    @Column(nullable = false)
    private Date lastLogin;

    @JsonIgnore
    @Column(nullable = false)
    private Date lastUpdated;

    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "account")
    private List<Trigger> triggers;

    public Account() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getVersion() {
        return version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }

    public Boolean isLocked() {
        return locked;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    public Boolean isExpired() {
        return expired;
    }

    public void setExpired(Boolean expired) {
        this.expired = expired;
    }

    public Boolean isDisabled() {
        return disabled;
    }

    public void setDisabled(Boolean disabled) {
        this.disabled = disabled;
    }

    public Date getLastLogin() {
        return lastLogin;
    }

    public void setLastLogin(Date lastLogin) {
        this.lastLogin = lastLogin;
    }

    public Date getLastUpdated() {
        return lastUpdated;
    }

    public void setLastUpdated(Date lastUpdated) {
        this.lastUpdated = lastUpdated;
    }

    public List<Trigger> getTriggers() {
        if (this.triggers == null) {
            this.triggers = Lists.newArrayList();
        }
        return triggers;
    }

    public void setTriggers(List<Trigger> triggers) {
        this.triggers = triggers;
    }

    public Trigger getTrigger(final Long triggerId) {
        Predicate<Trigger> isTrigger = new Predicate<Trigger>() {
            @Override
            public boolean apply(Trigger trigger) {
                return (trigger.getId().equals(triggerId));
            }
        };
        Iterable<Trigger> filteredTriggers = Iterables.filter(getTriggers(), isTrigger);
        return Iterables.getFirst(filteredTriggers, null);
    }

    public boolean removeTrigger(final Long triggerId) {
        return Iterables.removeIf(getTriggers(), new Predicate<Trigger>() {
            @Override
            public boolean apply(Trigger trigger) {
                boolean equals = trigger.getId().equals(triggerId);
                if (equals) {
                    trigger.setAccount(null);
                }
                return equals;
            }
        });
    }

    public void mergeBeforeUpdate(Account account) {
        this.username = (StringUtils.hasText(account.getUsername())) ? account.getUsername() : this.username;
        this.email = (StringUtils.hasText(account.getEmail())) ? account.getEmail() : this.email;
        this.firstname = (StringUtils.hasText(account.getFirstname())) ? account.getFirstname() : this.firstname;
        this.lastname = (StringUtils.hasText(account.getLastname())) ? account.getLastname() : this.lastname;
        this.password = (StringUtils.hasText(account.getPassword())) ? account.getPassword() : this.password;
        this.roles = (StringUtils.hasText(account.getRoles())) ? account.getRoles() : this.roles;
        this.locked = (account.isLocked() != null) ? account.isLocked() : this.locked;
        this.disabled = (account.isDisabled() != null) ? account.isDisabled() : this.disabled;
        this.expired = (account.isExpired() != null) ? account.isExpired() : this.expired;
        this.lastUpdated = new DateTime().toDate();
    }

    public void prepareBeforeSave() {
        this.lastLogin = new Date();
        this.lastUpdated = new Date();
        if (this.triggers != null) {
            for (Trigger trigger : this.triggers) {
                trigger.prepareBeforeSave();
            }
        }
    }
}
